package zdream.control.test.world.terrain;

import org.junit.Test;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import zdream.control.input.SimpleWorldInput;
import zdream.control.input.WorldInput;
import zdream.control.level.LevelManagerForTest;
import zdream.control.profile.mm.MegaManProfile;
import zdream.control.region.Region;
import zdream.control.region.SimpleRegionBuilder;
import zdream.control.region.TerrainConstant;
import zdream.control.world.LevelWorld;
import zdream.control.world.MFoe;
import zdream.control.world.MHookType;
import zdream.control.world.MHooks;
import zdream.control.world.TerrainHandler;
import zdream.utils.math.Point2d;

import static org.junit.Assert.*;

/**
 * @author Zdream
 * @date 2024-01-08
 * @since 0.0.1
 */
public class StabTerrainCase {

	@Test
	public void testBoxTerrainChecker() {
		Region region = new SimpleRegionBuilder(this.getClass().getSimpleName())
				.withRoomPositions("1")
				.withRoom(1)
						.terrain("#########################")
						.terrain("#########################")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##S                    ##")
						.terrain("#########################")
						.terrain("#########################")
						.terrain("#########################")
						.build()
				.withStartPoint('S')
				.build();
		System.out.println(region);
		assertEquals(1, region.rooms.length);

		MegaManProfile p1 = new MegaManProfile();

		LevelManagerForTest manager = LevelManagerForTest.builder()
				.withPlayer1(p1)
				.build();
		assertEquals(LevelWorld.STATE_NONE, manager.state);
		manager.applyWorld(region);
		manager.runUntil(ignored -> manager.state == LevelWorld.STATE_PLAYING);

		final MFoe player1 = manager.player1;
		assertNotNull(player1);
		// Test 1
		System.out.println("Test 1");
		Set<Point2d> points = new HashSet<>();
		Iterator<TerrainHandler.TerrainParam> it = player1.terrainHandler.checkBoxTerrain(player1);

		while (it.hasNext()) {
			final TerrainHandler.TerrainParam next = it.next();
			System.out.printf("(%d,%d)%n", next.bx, next.by);
			points.add(new Point2d(next.bx, next.by));
		}

		assertEquals(2, points.size());
		assertTrue(points.contains(new Point2d(2, 3)));
		assertTrue(points.contains(new Point2d(2, 4)));
		points.clear();

		// Test 2
		System.out.println("Test 2");
		player1.setAnchor(4.4f - player1.boxWidth - player1.boxX, player1.anchorY);
		player1.flush();
		assertEquals(4, (int) player1.pos.right());
		assertEquals(3, (int) player1.pos.left());
		manager.runOneFrame();

		it = player1.terrainHandler.checkBoxTerrain(player1);
		while (it.hasNext()) {
			final TerrainHandler.TerrainParam next = it.next();
			System.out.printf("(%d,%d)%n", next.bx, next.by);
			points.add(new Point2d(next.bx, next.by));
		}

		assertEquals(4, points.size());
		assertTrue(points.contains(new Point2d(3, 3)));
		assertTrue(points.contains(new Point2d(3, 4)));
		assertTrue(points.contains(new Point2d(4, 3)));
		assertTrue(points.contains(new Point2d(4, 4)));
	}

	@Test
	public void testTouchStab() {
		Region region = new SimpleRegionBuilder(this.getClass().getSimpleName())
				.withRoomPositions("1")
				.withRoom(1)
						.terrain("#########################")
						.terrain("#########################")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##    S      *         ##")
						.terrain("#########################")
						.terrain("#########################")
						.terrain("#########################")
						.build()
				.withStartPoint('S')
				.build();
		System.out.println(region);
		assertEquals(1, region.rooms.length);

		MegaManProfile p1 = new MegaManProfile();

		LevelManagerForTest manager = LevelManagerForTest.builder()
				.withPlayer1(p1)
				.build();
		assertEquals(LevelWorld.STATE_NONE, manager.state);
		manager.applyWorld(region);
		manager.runUntil(ignored -> manager.state == LevelWorld.STATE_PLAYING);
		assertEquals(TerrainConstant.TERRAIN_STAB_BALL, manager.currentRoom.terrains[13][3]);

		final MFoe player1 = manager.player1;
		assertNotNull(player1);
		float startX = player1.anchorX;

		System.out.println("尝试向右走, 直到碰刺");
		SimpleWorldInput input = new SimpleWorldInput();
		p1.setInput(input);
		input.setDown(WorldInput.MAP_RIGHT);

		manager.runUntil(ignored -> player1.anchorX > startX);
		manager.everyTick(ignored -> System.out.printf("%s%n", player1));

		AtomicBoolean damageReceived = new AtomicBoolean();
		player1.registerHook()
				.withType(MHookType.RECEIVE_DAMAGE)
				.handle(rawContext -> {
					MHooks.DamageContext context = rawContext.as();
					damageReceived.set(true);
					System.out.printf("收到伤害, damage=%d, level=%d%n", context.damage, context.level);
				})
				.withPriority(-9999)
				.buildAndSave();

		manager.runUntil(ignored -> player1.pos.right() > 13);
		assertFalse(player1.isDestroyed());
		input.setUp(WorldInput.MAP_RIGHT);

		// 手动修正 player1 的位置
		System.out.println("因为刺的边缘有一片区域是没有判定的 (约 0.2 格), 仅仅碰到这个外围区域是安全的");

		player1.setAnchor(13.1f - player1.boxWidth - player1.boxX, player1.anchorY);
		player1.flush();
		assertTrue(player1.pos.right() > 13);
		assertTrue(player1.pos.right() < 13.2);
		manager.runOneFrame();
		assertFalse(player1.isDestroyed());

		System.out.println("走到刺的核心区域就会寄");

		player1.setAnchor(13.3f - player1.boxWidth - player1.boxX, player1.anchorY);
		player1.flush();
		assertTrue(player1.pos.right() >= 13.3);
		manager.runOneFrame();
		assertTrue(player1.isDestroyed());
	}
}
